import { findPara, findFirstLeaf, findLastLeaf } from './find';
import { 
  ensureSelectContent,
  ensureSelectPreContent,
  isEmptyNode,
  ensureContent
} from './assert';
import { getContentNodesFromNodeOrFrag, getTextNodesFromNodeOrFrag } from './get';

function normalizeRange(range) {
  // console.log('before normalize', range.cloneRange());

  ensureSelectContent(range);
  ensureSelectPreContent(range);

  // console.log('after normalize', range.cloneRange());
}

function setStartBeforeFirstContent(range, node) {
  if(node.nodeType === Node.TEXT_NODE) {
    range.setStart(node, 0);
  }
  else {
    let contentNodes = getContentNodesFromNodeOrFrag(node);

    if(contentNodes.length === 0) {
      let head = findFirstLeaf(node);
      range.setStart(head, 0);
      return;
    }

    let head = contentNodes[0], parent = head.parentNode;
    if(head.nodeType === Node.TEXT_NODE) {
      range.setStart(head, 0);
    } else {
      range.setStart(parent, Array.from(parent.childNodes).indexOf(head));
    }
  }
}

function setEndAfterLastContent(range, node) {
  if(node.nodeType === Node.TEXT_NODE) {
    range.setEnd(node, node.nodeValue.length);
  }
  else {
    let contentNodes = getContentNodesFromNodeOrFrag(node);

    if(contentNodes.length === 0) {
      let tail = findLastLeaf(node);
      range.setEnd(tail, 0);
      return;
    }

    let tail = contentNodes[contentNodes.length - 1], parent = tail.parentNode;
    if(tail.nodeType === Node.TEXT_NODE) {
      range.setEnd(tail, tail.length);
    } else {
      range.setEnd(parent, Array.from(parent.childNodes).indexOf(tail) + 1);
    }
  }
}

function setStartAfterLastContent(range, node) {
  if(node.nodeType === Node.TEXT_NODE) {
    range.setStart(node, node.nodeValue.length);
  }
  else {
    let contentNodes = getContentNodesFromNodeOrFrag(node);

    if(contentNodes.length === 0) {
      let tail = findLastLeaf(node);
      range.setStart(tail, 0);
      return;
    }

    let tail = contentNodes[contentNodes.length - 1], parent = tail.parentNode;
    if(tail.nodeType === Node.TEXT_NODE) {
      range.setStart(tail, tail.length);
    } else {
      range.setStart(parent, Array.from(parent.childNodes).indexOf(tail) + 1);
    }
  }
}

function setEndBeforeFirstContent(range, node) {
  if(node.nodeType === Node.TEXT_NODE) {
    range.setEnd(node, 0);
  }
  else {
    let contentNodes = getContentNodesFromNodeOrFrag(node);

    if(contentNodes.length === 0) {
      let head = findFirstLeaf(node);
      range.setEnd(head, 0);
      return;
    }

    let head = contentNodes[0], parent = head.parentNode;
    if(head.nodeType === Node.TEXT_NODE) {
      range.setEnd(contentNodes[0], 0);
    } else {
      range.setEnd(parent, Array.from(parent.childNodes).indexOf(head));
    }
  }
}

function selectHead(range, node) {
  ensureContent(node);
  if(node.nodeType === Node.TEXT_NODE) {
    range.selectNodeContents(node);
    range.collapse(true);
  } else {
    let texts = getContentNodesFromNodeOrFrag(node);
    range.selectNodeContents(texts[0]);
    range.collapse(true);
  }
}

function selectTail(range, node) {
  ensureContent(node);
  if(node.nodeType === Node.TEXT_NODE) {
    range.selectNodeContents(node);
    range.collapse(false);
  } else {
    let texts = getContentNodesFromNodeOrFrag(node);
    range.selectNodeContents(texts[texts.length - 1]);
    range.collapse(false);
  }
}

function selectBlock(range, node) {
  if(isEmptyNode(node)) {
    let child = node.firstChild;
    let text = document.createTextNode("");
    child.appendChild(text);
    range.selectNodeContents(text);
  } else {
    let texts = getContentNodesFromNodeOrFrag(node);
    let head = texts[0], tail = texts[texts.length - 1],
      headParent = head.parentNode, tailParent = tail.parentNode;
    if(head.nodeType === Node.TEXT_NODE) {
      range.setStart(texts[0], 0);
    } else {
      range.setStart(headParent, Array.from(headParent.childNodes).indexOf(head));
    }

    if(tail.nodeType === Node.TEXT_NODE) {
      range.setEnd(texts[texts.length - 1], texts[texts.length - 1].nodeValue.length);
    } else {
      range.setEnd(tailParent, Array.from(tailParent.childNodes).indexOf(tail) + 1);
    }
  }
}

function selectLetter(range, node, index) {
  if(!(range instanceof Range) || !(node instanceof Node) || typeof index !== 'number') {
    console.warn(`selectLetter: range-${range}, node-${node}, index-${index}, parameter error, do nothing`);
    return;
  }
  let textNodes = getTextNodesFromNodeOrFrag(node);
  let len = 0;
  textNodes.forEach((text, idx) => {
    if(len < index && text.nodeValue.length + len > index) {
      let tidx = index - len;
      range.setStart(text, tidx);
      range.setEnd(text, tidx);
    }
  })
}

function setStartByTextIdx(range, node, index) {
  if(!(range instanceof Range) || !(node instanceof Node) || typeof index !== 'number') {
    console.warn(`setStartByTextIdx: range-${range}, node-${node}, index-${index}, parameter error, do nothing`);
    return;
  }
  let textNodes = getTextNodesFromNodeOrFrag(node);
  let len = 0;
  for(let i = 0; i < textNodes.length; i++) {
    let text = textNodes[i];
    if(len < index && text.nodeValue.length + len >= index) {
      let tidx = index - len;
      range.setStart(text, tidx);
      return;
    } else {
      len += text.nodeValue.length;
    }
  }
}

function setEndByTextIdx(range, node, index) {
  if(!(range instanceof Range) || !(node instanceof Node) || typeof index !== 'number') {
    console.warn(`setEndByTextIdx: range-${range}, node-${node}, index-${index}, parameter error, do nothing`);
    return;
  }
  let textNodes = getTextNodesFromNodeOrFrag(node);
  let len = 0;
  for(let i = 0; i < textNodes.length; i++) {
    let text = textNodes[i];
    if(len < index && text.nodeValue.length + len >= index) {
      let tidx = index - len;
      range.setEnd(text, tidx);
      return;
    } else {
      len += text.nodeValue.length;
    }
  }
}

export {
  normalizeRange,

  setStartBeforeFirstContent,
  setEndAfterLastContent,
  setStartAfterLastContent,
  setEndBeforeFirstContent,

  selectHead,
  selectTail,
  selectBlock,
  selectLetter,
  setStartByTextIdx,
  setEndByTextIdx
}